/*
 * Simple test file for cxxomfort's
 * "get<type> from std::tuple" feature extension.
 *
 * Interfaces tested in this example:
 *
 * * std::tuple
 * * std::get<type>(std::tuple<...>)
 * * cxxomfort::type_name
 * * static_assert
 *
 * A tuple type is selected, under the name 'TupleType'.
 * Invariant about the tuple (its status and types) are tested via static_assert.
 * After that, a variable of that tuple type is created, under the name 'tu'.
 * The get<index> and get<type> functions are used to obtain the member
 * components of this tuple, demonstrating the correctness of the latter.
 *
 * This and other examples assume cxxomfort is installed in
 * include library path; if otherwise, compile with
 * -I /path/to/cxxomfort .
 */

#include <cxxomfort/base.hpp>
#include <cxxomfort/tuple.hpp>
#include <cxxomfort/library/tuple.hpp> // is_tuple
#include <cxxomfort/library/utility.hpp>
//#include <cxxomfort/library/type_traits.hpp>
#include <cxxomfort/library/type_name.hpp>
#include <cxxomfort/array.hpp>
#include <cxxomfort/ostream.hpp>
#include <cassert>
#include <utility>
#include <string>
#include <iostream>

#if (!defined(__cpp_lib_tuples_by_type))
	#error "std::get<T> for std::tuple not detected or implemented"
#endif

// A tuple type is selected, under the name 'TupleType'.
typedef std::tuple<long int, std::pair<std::string, std::string>, cxxomfort::info, bool*, float > 
	TupleType;
size_t const TS = std::tuple_size<TupleType>::value;

namespace cxlt= cxxomfort::library::tuple;
//namespace cxltt = cxxomfort::library::type_traits;

int main () {
    using namespace std;
    cxxomfort::output_info(); cout<< endl;
    using cxxomfort::library::utility::is_std_pair;
    
    cout<< "Implementación de tuple get<type>: ";
    switch (CXXOMFORT_IMPLEMENTS_tuple_get_type) {
		case CXXO_IMPLSTATUS_NATIVE():
			cout<< "c++ native"; break;
		case CXXO_IMPLSTATUS_BACKPORT():
			cout<< "backported"; break;
		case CXXO_IMPLSTATUS_EMULATION():
			cout<< "emulated"; break;
		default: {
			cout<< "unknown?";
			break;
		}
	}
	cout<< endl;
    // Invariant about the tuple (its status and types) are tested via static_assert.
    static_assert (cxlt::is_tuple<TupleType>::value, "TupleType declaration must be a std::tuple<...>");
    static_assert (tuple_size<TupleType>::value > 2, "TupleType must have at least three (3) elements");

    cout<< "TupleType == "<< cxxomfort::type_name<TupleType>()<< endl;
    // After that, a variable of that tuple type is created, under the name 'tu'.

    TupleType tu = make_tuple(-1, make_pair("abc", "def"), cxxomfort::_, nullptr, 0);
    enum { e0assert = is_same< tuple_element<0,TupleType>::type, long int>::value };
    enum { e1assert = is_pointer< tuple_element<3,TupleType>::type >::value };

    static_assert (e0assert, "TupleType's first element (index <0>) must be 'long int'");
    static_assert (e1assert, "TupleType's fourth element (index <3>) must be a pointer type");

    // we get the type of the second element (index <1> )
    typedef tuple_element<0,TupleType>::type Tu0; // should be pair<string,string> here
    cout<< "Internal type with index<0> == "<< cxxomfort::type_name<Tu0>()<< endl;
    typedef tuple_element<1,TupleType>::type Tu1; // should be pair<string,string> here
    cout<< "Internal type with index<1> == "<< cxxomfort::type_name<Tu1>()<< endl;

    static_assert (is_std_pair<Tu1>::value, "TupleType's second element (index <1>) must be a std::pair<...>");

    // We check how many times the type appears in the tuple
    cout<< "Appears in tuple "<< cxlt::tuple_count_type<Tu1, TupleType>::value<< " times."<< endl;

    // The get<index> and get<type> functions are used to obtain the member
    // components of this tuple, demonstrating the correctness of the latter.
    long int g1 = get<0>(tu);
    long int t1 = get<long int>(tu);
    (void)g1;
    (void)t1;
    assert (g1 == t1 && "Obtained long ints from tuple are equal");
    std::string const& g2 = get<1>(tu).second;
    std::string const& t2 = get<Tu1>(tu).second;
    assert (g2 == t2 && "Obtained strings from tuple are equal");
    cout<< "Obtaining second string in the second element of tuple: ";
    cout<< g2<< endl;
    
    float& f1= get<float>(tu);
    cout<< "float: "<< f1<< endl;
    

    /*
     * When enabled, the following test *must* fail
     */
#if 0

#endif

    /*
     * When enabled, the following test *must* fail
     */
#if 0
    tuple<bool, int, double, int, char> repeated;
    get<3>(repeated); // gets the second int
    get<int>(repeated); // <-- must FAIL, there's more than one int in tuple
#endif


}


